package com.workcell.workdata.xthings.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.bean.copier.CopyOptions;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.ObjectUtil;
import com.github.pagehelper.PageInfo;
import com.google.common.collect.Lists;
import com.workcell.workdata.common.core.util.R;
import com.workcell.workdata.xthings.constant.CodeChainnumUseStateEnum;
import com.workcell.workdata.xthings.constant.HardwareTypeEnum;
import com.workcell.workdata.xthings.dto.*;
import com.workcell.workdata.xthings.entity.CodeChainnumUseStateData;
import com.workcell.workdata.xthings.entity.DeviceBind;
import com.workcell.workdata.xthings.service.*;
import com.workcell.workdata.xthings.utils.DateTimeUtil;
import lombok.RequiredArgsConstructor;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import java.text.SimpleDateFormat;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.concurrent.TimeUnit;

/**
 * @Author 廖博文
 * @Date 2023/2/7 16:54
 * @Description
 */
@Service
@RequiredArgsConstructor
public class IotMonitorReportServiceImpl implements IotMonitorReportService {

	private final BaseFlowmeterService baseFlowmeterSvc;

	private final BasePowermeterService basePowermeterSvc;

	private final BaseSerialDeviceService baseSerialDeviceService;

	private final CodeChainnumService codeChainnumService;

	private final CodeChainnumUseStateDataService codeChainnumUseStateDatSvc;
	private final DeviceBindService deviceBindService;

	private final RedisTemplate redisTemplate;
	private final String hardwarePercent = "IotMonitor_Report_HardwarePercent";

	private final String hardwareDataAnalysis = "IotMonitor_Report_HardwareDataAnalysis";
	private final String hardwareDataAnalysisNew = "IotMonitor_Report_HardwareDataAnalysis_new{}{}";
	private final String dataAnalysisFormat = "yyyy-MM-dd HH";

	@Override
	public R getHardwarePercent() {
		Object hardwarePercentObj = redisTemplate.opsForValue().get(String.format(hardwarePercent));
		if(ObjectUtil.isNotEmpty(hardwarePercentObj)){
			return R.ok(hardwarePercentObj);
		}

		IotMonitorReportResult.HardwarePercent customer = new IotMonitorReportResult.HardwarePercent();
		customer.setValue(2+"");
		customer.setName("2%");
		customer.setTitle("客户");

		IotMonitorReportResult.HardwarePercent device = new IotMonitorReportResult.HardwarePercent();
		Long codeChainnums = codeChainnumService.count();
		device.setValue(Long.toString(codeChainnums));
		device.setName(codeChainnums+"%");
		device.setTitle("总接入设备");

		IotMonitorReportResult.HardwarePercent deviceManufacturer = new IotMonitorReportResult.HardwarePercent();
		deviceManufacturer.setValue(2+"");
		deviceManufacturer.setName("2%");
		deviceManufacturer.setTitle("设备厂商");

		IotMonitorReportResult.HardwarePercent deviceModel = new IotMonitorReportResult.HardwarePercent();
		Long deviceModels = baseFlowmeterSvc.count() + basePowermeterSvc.count();
		deviceModel.setValue(Long.toString(deviceModels));
		deviceModel.setName(deviceModels+"%");
		deviceModel.setTitle("设备型号");

		IotMonitorReportResult.HardwarePercent deviceType = new IotMonitorReportResult.HardwarePercent();
		deviceType.setValue(3+"");
		deviceType.setName("3%");
		deviceType.setTitle("设备种类");

		List<IotMonitorReportResult.HardwarePercent> hardwarePercentList = Lists.newArrayList();
		hardwarePercentList.add(customer);
		hardwarePercentList.add(device);
		hardwarePercentList.add(deviceManufacturer);
		hardwarePercentList.add(deviceModel);
		hardwarePercentList.add(deviceType);
		redisTemplate.opsForValue().set(hardwarePercent, hardwarePercentList, 1 , TimeUnit.DAYS);
		return R.ok(hardwarePercentList);
	}

	@Override
	public R getNewHardwareData() {
		LocalDateTime startTime = LocalDateTime.of(LocalDate.now(), LocalTime.MIN);
		LocalDateTime endTime = LocalDateTime.of(LocalDate.now(), LocalTime.MAX);

		// 当前固定有流量计、功率计、TV盒子
		List<String> hardwareTypeNameList = new ArrayList<>();
		hardwareTypeNameList.add(HardwareTypeEnum.FLOW_METER.getMsg());
		hardwareTypeNameList.add(HardwareTypeEnum.POWER_METER.getMsg());
		hardwareTypeNameList.add("TV盒子");

		//每日新增设备
		List<Integer> newDataCount = Lists.newArrayList();
		newDataCount.add(codeChainnumService.getByTypeAndCreateTime(HardwareTypeEnum.FLOW_METER.getCode(), startTime, endTime));
		newDataCount.add(codeChainnumService.getByTypeAndCreateTime(HardwareTypeEnum.POWER_METER.getCode(), startTime, endTime));
		newDataCount.add(0);

		//总接入设备
		List<Integer> allDataCount = Lists.newArrayList();
		allDataCount.add(codeChainnumService.getByTypeAndUseState(HardwareTypeEnum.FLOW_METER.getCode(), null));
		allDataCount.add(codeChainnumService.getByTypeAndUseState(HardwareTypeEnum.POWER_METER.getCode(), null));
		allDataCount.add(0);

		Map<String, Object> map = new HashMap<>();
		map.put("titleList", hardwareTypeNameList);
		map.put("newDataCount", newDataCount);
		map.put("allDataCount",allDataCount);
		return R.ok(map);
	}

	@Override
	public R getHardwareData() {
		//流量计
		IotMonitorReportResult.HardwareData flowData = new IotMonitorReportResult.HardwareData();
		flowData.setTitle(HardwareTypeEnum.FLOW_METER.getMsg());
		flowData.setDevices(Long.valueOf(codeChainnumService.getByTypeAndUseState(HardwareTypeEnum.FLOW_METER.getCode(), null)));
		flowData.setDeviceManufacturer(1);
		flowData.setDeviceType(baseFlowmeterSvc.count());
		flowData.setInit(codeChainnumService.getByTypeAndUseState(HardwareTypeEnum.FLOW_METER.getCode(), CodeChainnumUseStateEnum.INIT.getCode()));
		flowData.setNormal(codeChainnumService.getByTypeAndUseState(HardwareTypeEnum.FLOW_METER.getCode(), CodeChainnumUseStateEnum.ONLINE.getCode()));
		flowData.setUnusual(codeChainnumService.getByTypeAndUseState(HardwareTypeEnum.FLOW_METER.getCode(), CodeChainnumUseStateEnum.OFFLINE.getCode()));

		//功率计
		IotMonitorReportResult.HardwareData powerData = new IotMonitorReportResult.HardwareData();
		powerData.setTitle(HardwareTypeEnum.POWER_METER.getMsg());
		powerData.setDevices(Long.valueOf(codeChainnumService.getByTypeAndUseState(HardwareTypeEnum.POWER_METER.getCode(), null)));
		powerData.setDeviceManufacturer(1);
		powerData.setDeviceType(basePowermeterSvc.count());
		powerData.setInit(codeChainnumService.getByTypeAndUseState(HardwareTypeEnum.POWER_METER.getCode(), CodeChainnumUseStateEnum.INIT.getCode()));
		powerData.setNormal(codeChainnumService.getByTypeAndUseState(HardwareTypeEnum.POWER_METER.getCode(), CodeChainnumUseStateEnum.ONLINE.getCode()));
		powerData.setUnusual(codeChainnumService.getByTypeAndUseState(HardwareTypeEnum.POWER_METER.getCode(), CodeChainnumUseStateEnum.OFFLINE.getCode()));

		//TV盒子
		IotMonitorReportResult.HardwareData beltScale = new IotMonitorReportResult.HardwareData();
		beltScale.setTitle("TV盒子");
		beltScale.setDevices(0L);
		beltScale.setDeviceManufacturer(0);
		beltScale.setDeviceType(0L);
		beltScale.setInit(0);
		beltScale.setNormal(0);
		beltScale.setUnusual(0);

		List<IotMonitorReportResult.HardwareData> hardwareDataList = Lists.newArrayList();
		hardwareDataList.add(flowData);
		hardwareDataList.add(powerData);
		hardwareDataList.add(beltScale);
		return R.ok(hardwareDataList);
	}

	@Override
	public R hardwareDataAnalysis() {
		Object hardwarePercentObj = redisTemplate.opsForValue().get(String.format(hardwareDataAnalysis));
		if(ObjectUtil.isNotEmpty(hardwarePercentObj)){
			return R.ok(hardwarePercentObj);
		}

		List<IotMonitorReportResult.HardwareDataAnalysis> analysisList = Lists.newArrayList();

		//流量计------------------------
		List<CodeChainnumUseStateData> flowUseStateList = codeChainnumUseStateDatSvc.getCountByTypeAndTime(HardwareTypeEnum.FLOW_METER.getCode(), null);
		IotMonitorReportResult.HardwareDataAnalysis  flowAnalysisList = new IotMonitorReportResult.HardwareDataAnalysis();
		flowAnalysisList.setTitle("流量计小时在线数分析");
		List<IotMonitorReportResult.HardwareDataAnalysisNum> flowNum = buildNumList();

		//功率计------------------------
		List<CodeChainnumUseStateData> powerUseStateList = codeChainnumUseStateDatSvc.getCountByTypeAndTime(HardwareTypeEnum.POWER_METER.getCode(), null);
		IotMonitorReportResult.HardwareDataAnalysis  powerAnalysisList = new IotMonitorReportResult.HardwareDataAnalysis();
		powerAnalysisList.setTitle("功率计小时在线数分析");
		List<IotMonitorReportResult.HardwareDataAnalysisNum> powerNum = buildNumList();

		//TV盒子------------------------
		IotMonitorReportResult.HardwareDataAnalysis beltAnalysisList = new IotMonitorReportResult.HardwareDataAnalysis();
		beltAnalysisList.setTitle("TV盒子小时在线数分析");
		List<IotMonitorReportResult.HardwareDataAnalysisNum> bletNum = buildNumList();

		// 获取当日凌晨
		SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd");
		String dateStr = simpleDateFormat.format(new Date())+" 00:00:00";
		for (int i = 0; i < 24; i++) {
			Date localHourTime = DateTimeUtil.dateAddHour(dateStr, i);
			packageHardwareAnalysis(flowNum.get(0).getNumber(), flowNum.get(1).getNumber(), flowUseStateList, localHourTime);
			packageHardwareAnalysis(powerNum.get(0).getNumber(), powerNum.get(1).getNumber(), powerUseStateList, localHourTime);
		}

		flowAnalysisList.setNumList(flowNum);
		powerAnalysisList.setNumList(powerNum);
		beltAnalysisList.setNumList(bletNum);
		analysisList.add(flowAnalysisList);
		analysisList.add(powerAnalysisList);
		analysisList.add(beltAnalysisList);
		redisTemplate.opsForValue().set(hardwareDataAnalysis, analysisList, 1 , TimeUnit.HOURS);
		return R.ok(analysisList);
	}

	@Override
	public R hardwareDataNew(String hardwareType) {
		HardwareTypeEnum hardwareTypeEnum = HardwareTypeEnum.getByCode(hardwareType);
		Assert.notEquals(hardwareTypeEnum,HardwareTypeEnum.OVER,"设备类型不存在");
		IotMonitorReportResult.HardwareData hardwareData = new IotMonitorReportResult.HardwareData();
		hardwareData.setTitle(hardwareTypeEnum.getMsg());
		hardwareData.setDevices(Long.valueOf(codeChainnumService.getByTypeAndUseState(hardwareTypeEnum.getCode(), null)));
//		hardwareData.setDeviceManufacturer(1);
//		hardwareData.setDeviceType(baseFlowmeterSvc.count());
		hardwareData.setInit(codeChainnumService.getByTypeAndUseState(hardwareTypeEnum.getCode(), CodeChainnumUseStateEnum.INIT.getCode()));
		hardwareData.setNormal(codeChainnumService.getByTypeAndUseState(hardwareTypeEnum.getCode(), CodeChainnumUseStateEnum.ONLINE.getCode()));
		hardwareData.setUnusual(codeChainnumService.getByTypeAndUseState(hardwareTypeEnum.getCode(), CodeChainnumUseStateEnum.OFFLINE.getCode()));
		return R.ok(hardwareData);
	}

	@Override
	public R hardwareDataAnalysisNew(String hardwareType) {
		// 获取设备枚举
		HardwareTypeEnum hardwareTypeEnum = HardwareTypeEnum.getByCode(hardwareType);
		Assert.notEquals(hardwareTypeEnum,HardwareTypeEnum.OVER,"设备类型不存在");
		String format = DateUtil.format(new Date(), dataAnalysisFormat);
		//判断是否存在缓存，若有取缓存数据
//		Object hardwarePercentObj = redisTemplate.opsForValue().get(StrUtil.format(hardwareDataAnalysisNew,hardwareTypeEnum.getCode(),format));
//		if(ObjectUtil.isNotEmpty(hardwarePercentObj)){
//			return R.ok(hardwarePercentObj);
//		}
		// 查询该设备的在线记录
		List<CodeChainnumUseStateData> useStateList = codeChainnumUseStateDatSvc.getCountByTypeAndTime(hardwareTypeEnum.getCode(), null);
		// 最终结果集
		IotMonitorReportResult.HardwareDataAnalysis  hardwareDataAnalysis = new IotMonitorReportResult.HardwareDataAnalysis();
		// 标题
		hardwareDataAnalysis.setTitle(hardwareTypeEnum.getMsg());
		// 初始化展示数据
		List<IotMonitorReportResult.HardwareDataAnalysisNum> hardwareDataAnalysisNums = buildNumList();
		// 获取当日凌晨
		String dateStr = DateUtil.formatDate(new Date())+" 00:00:00";
		for (int i = 0; i < 24; i++) {
			// 将记录按时间归置（整点）
			Date localHourTime = DateTimeUtil.dateAddHour(dateStr, i);
			if (localHourTime.after(new Date())){
				break;
			}
			packageHardwareAnalysis(hardwareDataAnalysisNums.get(0).getNumber(), hardwareDataAnalysisNums.get(1).getNumber(), useStateList, localHourTime);
		}
		// 将数据放入结果集
		hardwareDataAnalysis.setNumList(hardwareDataAnalysisNums);
		// 放入缓存
//		redisTemplate.opsForValue().set(StrUtil.format(hardwareDataAnalysisNew,hardwareTypeEnum.getCode(),format), hardwareDataAnalysis, 1 , TimeUnit.HOURS);
		return R.ok(hardwareDataAnalysis);
	}

	@Override
	public PageInfo<SerialDeviceMonitorDto> serialDeviceMonitorInfoPage(PageVo pageVO, DeviceMonitorQueryVo deviceMonitorQueryVo) {
		return baseSerialDeviceService.serialDeviceMonitorInfoPage(pageVO, deviceMonitorQueryVo);
	}

	@Override
	public PageInfo<SerialDeviceMonitorBindDto> serialDeviceMonitorBindPage(PageVo pageVO, DeviceMonitorBindQueryVo deviceMonitorQueryVo) {
		return baseSerialDeviceService.serialDeviceMonitorBindPage(pageVO, deviceMonitorQueryVo);
	}

	@Override
	public CodeChainnumVo.SelectDeviceInfo getDeviceRunInfo(String deviceNo, String manufacturerCode, String hardwareType) {

		//设备基础信息
		Map<String, Object> map = codeChainnumService.deviceDetail(deviceNo, hardwareType);
		CodeChainnumVo.SelectDeviceInfo selectDeviceInfo = BeanUtil.mapToBean(map, CodeChainnumVo.SelectDeviceInfo.class, CopyOptions.create().ignoreError());
		//是否绑定
		DeviceBind one = deviceBindService.getOne(DeviceBind.builder().slaveDeviceNo(deviceNo).build());
		if (ObjectUtil.isEmpty(one)){
			selectDeviceInfo.setBindStatus("未绑定");
			return selectDeviceInfo;
		}

		//串口设备信息
		Map<String, Object> map2 = codeChainnumService.deviceDetail(one.getMasterDeviceNo(), one.getMasterHardwareType());
		String hardwareName = HardwareTypeEnum.getByCode(one.getMasterHardwareType()).getMsg();
		String dcProductType = ObjectUtil.isNotEmpty(map2.get("dcProductType"))?(String) map2.get("dcProductType"):"";
		String masterUseStatus = ObjectUtil.isNotEmpty(map2.get("useState"))?(String) map2.get("useState"):"";

		selectDeviceInfo.setBindStatus("已绑定")
				.setMasterDeviceNo(one.getMasterDeviceNo())
				.setMasterDeviceName(hardwareName)
				.setMasterProduct(dcProductType)
				.setMasterUseStatus(masterUseStatus);
		return selectDeviceInfo;
	}

	private static void packageHardwareAnalysis(List<Long> onlineNumber, List<Long> offlineNumber, List<CodeChainnumUseStateData> useStateData, Date dateTime) {
		DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyyMMddHH");
		SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyyMMddHH");
		String formatDateTime = simpleDateFormat.format(dateTime);
		onlineNumber.add(useStateData.stream().filter(data -> data.getScheduledTime().format(dateTimeFormatter).equals(formatDateTime)
				&& data.getUseState().equals(CodeChainnumUseStateEnum.ONLINE.getCode())).count());

		offlineNumber.add(useStateData.stream().filter(data ->data.getScheduledTime().format(dateTimeFormatter).equals(formatDateTime)
				&& data.getUseState().equals(CodeChainnumUseStateEnum.OFFLINE.getCode())).count());
	}

	private static List<IotMonitorReportResult.HardwareDataAnalysisNum> buildNumList(){
		List<IotMonitorReportResult.HardwareDataAnalysisNum> numList = Lists.newArrayList();
		//online
		IotMonitorReportResult.HardwareDataAnalysisNum  onlineNumber = new IotMonitorReportResult.HardwareDataAnalysisNum();
		onlineNumber.setType(CodeChainnumUseStateEnum.ONLINE.getMsg());
		onlineNumber.setNumber(Lists.newArrayList());
		numList.add(onlineNumber);

		//offline
		IotMonitorReportResult.HardwareDataAnalysisNum  offlineNumber = new IotMonitorReportResult.HardwareDataAnalysisNum();
		offlineNumber.setType(CodeChainnumUseStateEnum.OFFLINE.getMsg());
		offlineNumber.setNumber(Lists.newArrayList());
		numList.add(offlineNumber);
		return numList;
	}
}
